/**
 * @file ATsender.c
 * @brief
 * @author ChenXiying (floodchen@foxmail.com)
 * @version 1.0
 * @date 2023-03-07
 *
 * @copyright Copyright (c) 2023  Xidian University
 *
 */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define BUF_SIZE      8192
#define DELAY_US      500000
#define RET_FAILED    -1
#define RET_SUCCESS   0

int serial_fd;
const char err_req[4] = {'+', 'E', 'R', 'R'};
const char ok_req[3] = {'+', 'o', 'k'};
typedef struct
{
    char *cmd;
    char *request;
} AT_CMD;

static AT_CMD at_cmd[] = {
    {"+++", "a"},
    {"a", "+ok"},
    {"AT+E=off\n", "+ok"},
    {"AT+WMODE=STA\n", "+ok"},
    {"AT+WSTA=tdr_wifi,12345678\n", "+ok"},
    {"AT+TMODE=HTPC\n", "+ok"},
    {"AT+HTPTP=POST\n", "+ok"},
    {"AT+HTPSV=39.103.189.45,8088\n", "+ok"},
    {"AT+HTPURL=/transfer/data\n", "+ok"},
    {"AT+HTPTO=10\n", "+ok"},
    {"AT+HTPFT=off\n", "+ok"},
    {"AT+HTPHD=Accept:application/octet-stream[0D][0A]Accept-Language:zh-CN[0D][0A]User-Agent:Mozilla/5.0[0D][0A]Connection: Keep-Alive[0D][0A]\n", // text/html
     "+ok"},
    {"AT+CFGTF\n", "+ok=F-Setting Saved"},
    {"AT+ENTM\n", "+ok"},
    NULL};
int init_serial_port(char *portname, int baudrate)
{
    int fd;
    struct termios tty;

    // 打开串口设备文件
    if ((fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC)) < RET_SUCCESS) {
        perror("open");
        return RET_FAILED;
    }

    // 获取并修改串口参数
    memset(&tty, 0, sizeof(tty));
    if (tcgetattr(fd, &tty) != RET_SUCCESS) {
        perror("tcgetattr");
        return RET_FAILED;
    }

    cfsetospeed(&tty, baudrate);
    cfsetispeed(&tty, baudrate);

    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;
    tty.c_cflag &= ~PARENB;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;

    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_iflag &= ~(ICRNL | INLCR);
    tty.c_oflag &= ~OPOST;

    tty.c_cc[VTIME] = 10;
    tty.c_cc[VMIN] = 0;

    if (tcsetattr(fd, TCSANOW, &tty) != RET_SUCCESS) {
        perror("tcsetattr");
        return RET_FAILED;
    }

    return fd;
}

int write_to_serial_port(const char *cmd)
{
    int len = write(serial_fd, cmd, strlen(cmd));
    if (len < 0) {
        perror("write");
        return RET_FAILED;
    }
    printf("T:%s", cmd);
    fflush(stdout);
    return len;
}

int read_from_serial_port(char *buf)
{
    int len = read(serial_fd, buf, BUF_SIZE - 1);
    if (len < 0) {
        perror("read");
        return RET_FAILED;
    }
    buf[len] = '\0';

    int i = 0;
    while (i < len) {
        buf[i] = (buf[i] == 0xd || buf[i] == 0xa) ? 0 : buf[i];
        i++;
    }
    buf[len] = 0xd;

    printf("R:%s\n", buf);
    fflush(stdout);
    return len;
}

int verify_from_read(const char *buf, const char *verify)
{
    if (strstr(buf, verify) != NULL)
        return RET_SUCCESS;
    return RET_FAILED;
}

int send_command(const char *cmd, const char *verify)
{
    char buf[BUF_SIZE];
    if (write_to_serial_port(cmd) < 0) {
        return RET_FAILED;
    }
    usleep(DELAY_US);
    if (read_from_serial_port(buf) < 0) {
        return RET_FAILED;
    }

    if (RET_SUCCESS == verify_from_read(buf, verify)) {
        printf("cmd success\n");
        return RET_SUCCESS;
    } else {
        printf("cmd failed\n");
        return RET_FAILED;
    }
}

int main(int argc, char *argv[])
{
    char *portname = "/dev/ttymxc1"; // 串口设备名称
    int baudrate = B115200;          // 波特率
    int loop = 1;                    // 循环次数
    AT_CMD *p = at_cmd;

    if (1 == argc) {
        printf("Default port %s, default baudrate 115200\n", portname);
        printf("Usage: ./ATsender <portname> <baudrate>\n");
    } else if (3 == argc) {
        portname = argv[1];
        baudrate = atoi(argv[2]);
    } else {
        printf("Usage: ./ATsender <portname> <baudrate>\n");
        return RET_FAILED;
    }

    serial_fd = init_serial_port(portname, baudrate);
    if (serial_fd < 0) {
        return RET_FAILED;
    }
    while (p->cmd != NULL) {
        if (RET_FAILED == send_command((const char *)p->cmd, (const char *)p->request)) {
            send_command("\n", err_req);
            send_command("AT+E=off\n", ok_req);
            send_command("AT+ENTM\n", ok_req);
            p = at_cmd;
        } else {
            p++;
        }
        loop++;
        if (loop == 0xff) {
            printf("init error, please try again\n");
            return RET_FAILED;
        }
    }
    printf("init success\n");
    return RET_SUCCESS;
}
